package org.lora.mvc.security.impl;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.lora.Constant;
import org.lora.core.common.CommonCoreServiceImpl;
import org.lora.core.configxml.entity.WebSecurityConfig;
import org.lora.core.context.WebContext;
import org.lora.exception.WebSecurityException;
import org.lora.mvc.security.ISecurityService;
import org.lora.mvc.security.entity.Principal;
import org.lora.mvc.security.entity.WebResource;
import org.lora.util.StringUtil;
import org.lora.util.ValidateUtil;

/**
 * <p>
 * 类名:AbsSercurityService
 * </p>
 * 描述:abstract实现<br>
 * 创建时间:2016年7月30日 下午5:14:24<br>
 * 
 * @author Bladnir@outlook.com<br>
 *         修改历史:<br>
 *         2016年7月30日 Bladnir 创建<br>
 */
public abstract class AbsSercurityService extends CommonCoreServiceImpl implements ISecurityService {

	/**
	 * 登陆验证 如果返回Principal就算通过 如果返回null就算不通过
	 * 
	 * @param request
	 * @param response
	 * @param principal
	 * @return
	 */
	protected abstract Principal loginValidate(HttpServletRequest request, HttpServletResponse response, Principal principal);

	/**
	 * 验证权限
	 * 
	 * @param request
	 * @param response
	 * @param principal
	 * @param webResource
	 * @return
	 */
	protected abstract boolean validateRequestHandle(HttpServletRequest request, HttpServletResponse response, Principal principal,
			WebResource webResource);

	/**
	 * 权限验证失败处理
	 * 
	 * @param request
	 * @param response
	 * @param webResource
	 */
	protected abstract void validateFailHandle(HttpServletRequest request, HttpServletResponse response, WebResource webResource);

	/**
	 * 登入成功处理
	 * 
	 * @param request
	 * @param response
	 */
	protected abstract void loginSuccessHandle(HttpServletRequest request, HttpServletResponse response);

	/**
	 * 登入失败处理
	 * 
	 * @param request
	 * @param response
	 */
	protected abstract void loginFailHandle(HttpServletRequest request, HttpServletResponse response);

	/**
	 * 登出处理
	 * 
	 * @param request
	 * @param response
	 */
	protected abstract void logoutHandle(HttpServletRequest request, HttpServletResponse response);

	/**
	 * 登入处理
	 * 
	 * @param request
	 * @param response
	 * @throws WebSecurityException
	 */
	protected void loginRequest(HttpServletRequest request, HttpServletResponse response) throws WebSecurityException {
		String localAddr = request.getLocalAddr();
		Principal principal = new Principal();
		principal.setLocalAddr(localAddr);

		principal = loginValidate(request, response, principal);
		if (principal == null) {
			loginFailHandle(request, response);
		} else {
			// 放入主体信息到Session
			HttpSession session = request.getSession();
			session.setAttribute(Constant.SECURITY_SESSION_KEY, principal);
			// 登入成功处理
			loginSuccessHandle(request, response);
		}

	}

	/**
	 * 登出处理
	 * 
	 * @param request
	 * @param response
	 * @throws WebSecurityException
	 */
	protected void logoutRequest(HttpServletRequest request, HttpServletResponse response) throws WebSecurityException {
		// 清除session
		HttpSession session = request.getSession();
		session.invalidate();
		// 登出处理
		logoutHandle(request, response);
	}

	/**
	 * 权限验证方法
	 * 
	 * @param request
	 * @param response
	 * @return
	 * @throws WebSecurityException
	 */
	protected boolean validateRequest(HttpServletRequest request, HttpServletResponse response) throws WebSecurityException {

		HttpSession session = request.getSession();

		Object value = session.getAttribute(Constant.SECURITY_SESSION_KEY);
		WebResource webResource = getWebResourceFromRequest(request);

		if (value == null) {
			super.systemLog.debug("[" + webResource.getUrl() + "] user not login");
			return false;
		} else {
			Principal principal;
			if (value instanceof Principal) {
				principal = (Principal) value;
			} else {
				super.systemLog.debug("session value is not Principal " + value);
				return false;
			}

			if (validatePermitAll(webResource)) {
				super.systemLog.debug("[" + webResource.getUrl() + "] PermitAll pass");
				return true;
			} else {
				return validateRequestHandle(request, response, principal, webResource);
			}
		}

	}

	/**
	 * 验证是不是PermitAll权限对应的资源
	 * 
	 * @param webResource
	 * @return
	 */
	protected boolean validatePermitAll(WebResource webResource) {
		WebSecurityConfig webSecurityConfig = WebContext.getInstance().getConfigXmlEntity().getWebSecurityConfig();
		List<String> list = webSecurityConfig.getPermitAllList();

		boolean flag = false;

		for (String url : list) {
			if (webResource.getUrl().startsWith(url)) {
				flag = true;
				break;
			}
		}

		return flag;
	}

	/**
	 * 验证是不是PermitNo权限对应的资源
	 * 
	 * @param webResource
	 * @return
	 */
	protected boolean validatePermitNo(WebResource webResource) {
		WebSecurityConfig webSecurityConfig = WebContext.getInstance().getConfigXmlEntity().getWebSecurityConfig();
		List<String> list = webSecurityConfig.getNoSecurityUrlList();

		boolean flag = false;

		for (String url : list) {
			if (webResource.getUrl().startsWith(url) || StringUtil.isNull(webResource.getUrl()) || "/".equals(webResource.getUrl())) {
				flag = true;
				break;
			}
		}

		return flag;
	}

	/**
	 * 从request中获取WebResource对象
	 * 
	 * @param request
	 * @return
	 */
	protected WebResource getWebResourceFromRequest(HttpServletRequest request) {
		String path = request.getContextPath();
		String requestURI = request.getRequestURI();
		String requestString = requestURI.replace(path, StringUtil.BLANK).replace(".do", StringUtil.BLANK);

		WebResource resource = new WebResource();

		resource.setUrl(requestString);

		Map<String, String> paramMap = new HashMap<>();

		for (Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
			String key = entry.getKey();

			StringBuilder val = new StringBuilder();

			for (String value : entry.getValue()) {
				val = val.append(value).append("|");
			}

			String valString = val.toString();
			valString = valString.substring(0, valString.length() - 1);

			paramMap.put(key, valString);

		}

		resource.setParamMap(paramMap);

		return resource;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.lora.mvc.security.ISecurityService#doFilter(javax.servlet.ServletRequest
	 * , javax.servlet.ServletResponse, javax.servlet.FilterChain)
	 * 
	 * SecurityFilter 的入口 此处或许以后需要考虑加入链式过滤机制
	 */
	@Override
	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws WebSecurityException {

		HttpServletRequest request;
		HttpServletResponse response;
		if (req instanceof HttpServletRequest) {
			request = (HttpServletRequest) req;
		} else {
			throw new WebSecurityException(req + " not instanceof HttpServletRequest");
		}

		if (res instanceof HttpServletResponse) {
			response = (HttpServletResponse) res;
		} else {
			throw new WebSecurityException(res + " not instanceof HttpServletResponse");
		}

		WebSecurityConfig webSecurityConfig = WebContext.getInstance().getConfigXmlEntity().getWebSecurityConfig();
		// 登陆url
		String loginUrl = webSecurityConfig.getLoginUrl();
		// 登出url
		String logoutUrl = webSecurityConfig.getLogoutUrl();

		// 从request中获取WebResource对象
		WebResource webResource = getWebResourceFromRequest(request);
		if (ValidateUtil.isEqual(loginUrl, webResource.getUrl())) {
			// 登入处理
			super.systemLog.debug("[" + webResource.getUrl() + "] is loginUrl");
			loginRequest(request, response);
		} else if (ValidateUtil.isEqual(logoutUrl, webResource.getUrl())) {
			// 登出处理
			super.systemLog.debug("[" + webResource.getUrl() + "] is logoutUrl");
			logoutRequest(request, response);
		} else if (validatePermitNo(webResource)) {
			// 不需要验证权限
			// 交由MVC模块的DistributeServlet处理
			//super.systemLog.debug("[" + webResource.getUrl() + "] need no permit");
			continueFilter(req, res, chain);
		} else {
			// 权限验证处理
			if (validateRequest(request, response)) {
				// 验证成功后交由MVC模块的DistributeServlet处理
				continueFilter(req, res, chain);
			} else {
				// 验证失败处理
				validateFailHandle(request, response, webResource);
			}
		}

	}

	/**
	 * 继续过滤
	 * 
	 * @param req
	 * @param res
	 * @param chain
	 * @throws WebSecurityException
	 */
	protected void continueFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws WebSecurityException {
		try {
			chain.doFilter(req, res);
		} catch (ServletException | IOException e) {
			throw new WebSecurityException(e.getMessage(), e);
		}
	}
}
